import { confirm, input, select } from "@inquirer/prompts";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { basename, join, resolve } from "path";
import type { z } from "zod";

// Import the schema for DEFAULT_MANIFEST_VERSION
// TODO: Allow dynamic manifest version choice
import type { McpbManifestSchema } from "../schemas/0.2.js";
import { DEFAULT_MANIFEST_VERSION } from "../shared/constants.js";

interface PackageJson {
  name?: string;
  version?: string;
  description?: string;
  main?: string;
  author?: string | { name?: string; email?: string; url?: string };
  repository?: string | { type?: string; url?: string };
  license?: string;
}

export function readPackageJson(dirPath: string): PackageJson {
  const packageJsonPath = join(dirPath, "package.json");
  if (existsSync(packageJsonPath)) {
    try {
      return JSON.parse(readFileSync(packageJsonPath, "utf-8"));
    } catch (e) {
      // Ignore package.json parsing errors
    }
  }
  return {};
}

export function getDefaultAuthorName(packageData: PackageJson): string {
  if (typeof packageData.author === "string") {
    return packageData.author;
  }
  return packageData.author?.name || "";
}

export function getDefaultAuthorEmail(packageData: PackageJson): string {
  if (typeof packageData.author === "object") {
    return packageData.author?.email || "";
  }
  return "";
}

export function getDefaultAuthorUrl(packageData: PackageJson): string {
  if (typeof packageData.author === "object") {
    return packageData.author?.url || "";
  }
  return "";
}

export function getDefaultRepositoryUrl(packageData: PackageJson): string {
  if (typeof packageData.repository === "string") {
    return packageData.repository;
  }
  return packageData.repository?.url || "";
}

export function getDefaultBasicInfo(
  packageData: PackageJson,
  resolvedPath: string,
) {
  const name = packageData.name || basename(resolvedPath);
  const authorName = getDefaultAuthorName(packageData) || "Unknown Author";
  const displayName = name;
  const version = packageData.version || "1.0.0";
  const description = packageData.description || "A MCPB bundle";

  return { name, authorName, displayName, version, description };
}

export function getDefaultAuthorInfo(packageData: PackageJson) {
  return {
    authorEmail: getDefaultAuthorEmail(packageData),
    authorUrl: getDefaultAuthorUrl(packageData),
  };
}

export function getDefaultServerConfig(packageData?: PackageJson) {
  const serverType = "node" as const;
  const entryPoint = getDefaultEntryPoint(serverType, packageData);
  const mcp_config = createMcpConfig(serverType, entryPoint);

  return { serverType, entryPoint, mcp_config };
}

export function getDefaultOptionalFields(packageData: PackageJson) {
  return {
    keywords: "",
    license: packageData.license || "MIT",
    repository: undefined,
  };
}

export function createMcpConfig(
  serverType: "node" | "python" | "binary",
  entryPoint: string,
): {
  command: string;
  args: string[];
  env?: Record<string, string>;
} {
  switch (serverType) {
    case "node":
      return {
        command: "node",
        args: ["${__dirname}/" + entryPoint],
        env: {},
      };
    case "python":
      return {
        command: "python",
        args: ["${__dirname}/" + entryPoint],
        env: {
          PYTHONPATH: "${__dirname}/server/lib",
        },
      };
    case "binary":
      return {
        command: "${__dirname}/" + entryPoint,
        args: [],
        env: {},
      };
  }
}

export function getDefaultEntryPoint(
  serverType: "node" | "python" | "binary",
  packageData?: PackageJson,
): string {
  switch (serverType) {
    case "node":
      return packageData?.main || "server/index.js";
    case "python":
      return "server/main.py";
    case "binary":
      return "server/my-server";
  }
}

export async function promptBasicInfo(
  packageData: PackageJson,
  resolvedPath: string,
) {
  const defaultName = packageData.name || basename(resolvedPath);

  const name = await input({
    message: "Extension name:",
    default: defaultName,
    validate: (value) => value.trim().length > 0 || "Name is required",
  });

  const authorName = await input({
    message: "Author name:",
    default: getDefaultAuthorName(packageData),
    validate: (value) => value.trim().length > 0 || "Author name is required",
  });

  const displayName = await input({
    message: "Display name (optional):",
    default: name,
  });

  const version = await input({
    message: "Version:",
    default: packageData.version || "1.0.0",
    validate: (value) => {
      if (!value.trim()) return "Version is required";
      if (!/^\d+\.\d+\.\d+/.test(value)) {
        return "Version must follow semantic versioning (e.g., 1.0.0)";
      }
      return true;
    },
  });

  const description = await input({
    message: "Description:",
    default: packageData.description || "",
    validate: (value) => value.trim().length > 0 || "Description is required",
  });

  return { name, authorName, displayName, version, description };
}

export async function promptAuthorInfo(packageData: PackageJson) {
  const authorEmail = await input({
    message: "Author email (optional):",
    default: getDefaultAuthorEmail(packageData),
  });

  const authorUrl = await input({
    message: "Author URL (optional):",
    default: getDefaultAuthorUrl(packageData),
  });

  return { authorEmail, authorUrl };
}

export async function promptServerConfig(packageData?: PackageJson) {
  const serverType = (await select({
    message: "Server type:",
    choices: [
      { name: "Node.js", value: "node" },
      { name: "Python", value: "python" },
      { name: "Binary", value: "binary" },
    ],
    default: "node",
  })) as "node" | "python" | "binary";

  const entryPoint = await input({
    message: "Entry point:",
    default: getDefaultEntryPoint(serverType, packageData),
  });

  const mcp_config = createMcpConfig(serverType, entryPoint);

  return { serverType, entryPoint, mcp_config };
}

export async function promptTools() {
  const addTools = await confirm({
    message:
      "Does your MCP Server provide tools you want to advertise (optional)?",
    default: true,
  });

  const tools: Array<{ name: string; description?: string }> = [];
  let toolsGenerated = false;

  if (addTools) {
    let addMore = true;
    while (addMore) {
      const toolName = await input({
        message: "Tool name:",
        validate: (value) => value.trim().length > 0 || "Tool name is required",
      });
      const toolDescription = await input({
        message: "Tool description (optional):",
      });

      tools.push({
        name: toolName,
        ...(toolDescription ? { description: toolDescription } : {}),
      });

      addMore = await confirm({
        message: "Add another tool?",
        default: false,
      });
    }

    // Ask about generated tools
    toolsGenerated = await confirm({
      message: "Does your server generate additional tools at runtime?",
      default: false,
    });
  }

  return { tools, toolsGenerated };
}

export async function promptPrompts() {
  const addPrompts = await confirm({
    message:
      "Does your MCP Server provide prompts you want to advertise (optional)?",
    default: false,
  });

  const prompts: Array<{
    name: string;
    description?: string;
    arguments?: string[];
    text: string;
  }> = [];
  let promptsGenerated = false;

  if (addPrompts) {
    let addMore = true;
    while (addMore) {
      const promptName = await input({
        message: "Prompt name:",
        validate: (value) =>
          value.trim().length > 0 || "Prompt name is required",
      });
      const promptDescription = await input({
        message: "Prompt description (optional):",
      });

      // Ask about arguments
      const hasArguments = await confirm({
        message: "Does this prompt have arguments?",
        default: false,
      });

      const argumentNames: string[] = [];
      if (hasArguments) {
        let addMoreArgs = true;
        while (addMoreArgs) {
          const argName = await input({
            message: "Argument name:",
            validate: (value) => {
              if (!value.trim()) return "Argument name is required";
              if (argumentNames.includes(value)) {
                return "Argument names must be unique";
              }
              return true;
            },
          });
          argumentNames.push(argName);

          addMoreArgs = await confirm({
            message: "Add another argument?",
            default: false,
          });
        }
      }

      // Prompt for the text template
      const promptText = await input({
        message: hasArguments
          ? `Prompt text (use \${arguments.name} for arguments: ${argumentNames.join(", ")}):`
          : "Prompt text:",
        validate: (value) =>
          value.trim().length > 0 || "Prompt text is required",
      });

      prompts.push({
        name: promptName,
        ...(promptDescription ? { description: promptDescription } : {}),
        ...(argumentNames.length > 0 ? { arguments: argumentNames } : {}),
        text: promptText,
      });

      addMore = await confirm({
        message: "Add another prompt?",
        default: false,
      });
    }

    // Ask about generated prompts
    promptsGenerated = await confirm({
      message: "Does your server generate additional prompts at runtime?",
      default: false,
    });
  }

  return { prompts, promptsGenerated };
}

export async function promptOptionalFields(packageData: PackageJson) {
  const keywords = await input({
    message: "Keywords (comma-separated, optional):",
    default: "",
  });

  const license = await input({
    message: "License:",
    default: packageData.license || "MIT",
  });

  const addRepository = await confirm({
    message: "Add repository information?",
    default: !!packageData.repository,
  });

  let repository: { type: string; url: string } | undefined;
  if (addRepository) {
    const repoUrl = await input({
      message: "Repository URL:",
      default: getDefaultRepositoryUrl(packageData),
    });
    if (repoUrl) {
      repository = {
        type: "git",
        url: repoUrl,
      };
    }
  }

  return { keywords, license, repository };
}

export async function promptLongDescription(description: string) {
  const hasLongDescription = await confirm({
    message: "Add a detailed long description?",
    default: false,
  });

  if (hasLongDescription) {
    const longDescription = await input({
      message: "Long description (supports basic markdown):",
      default: description,
    });
    return longDescription;
  }

  return undefined;
}

export async function promptUrls() {
  const homepage = await input({
    message: "Homepage URL (optional):",
    validate: (value) => {
      if (!value.trim()) return true;
      try {
        new URL(value);
        return true;
      } catch {
        return "Must be a valid URL (e.g., https://example.com)";
      }
    },
  });

  const documentation = await input({
    message: "Documentation URL (optional):",
    validate: (value) => {
      if (!value.trim()) return true;
      try {
        new URL(value);
        return true;
      } catch {
        return "Must be a valid URL";
      }
    },
  });

  const support = await input({
    message: "Support URL (optional):",
    validate: (value) => {
      if (!value.trim()) return true;
      try {
        new URL(value);
        return true;
      } catch {
        return "Must be a valid URL";
      }
    },
  });

  return { homepage, documentation, support };
}

export async function promptVisualAssets() {
  const icon = await input({
    message: "Icon file path (optional, relative to manifest):",
    validate: (value) => {
      if (!value.trim()) return true;
      if (value.includes("..")) return "Relative paths cannot include '..'";
      return true;
    },
  });

  const addIconVariants = await confirm({
    message: "Add theme/size-specific icons array?",
    default: false,
  });

  const icons: Array<{
    src: string;
    size: string;
    theme?: string;
  }> = [];

  if (addIconVariants) {
    let addMoreIcons = true;
    while (addMoreIcons) {
      const iconSrc = await input({
        message: "Icon source path (relative to manifest):",
        validate: (value) => {
          if (!value.trim()) return "Icon path is required";
          if (value.includes("..")) return "Relative paths cannot include '..'";
          return true;
        },
      });

      const iconSize = await input({
        message: "Icon size (e.g., 16x16):",
        validate: (value) => {
          if (!value.trim()) return "Icon size is required";
          if (!/^\d+x\d+$/.test(value)) {
            return "Icon size must be in WIDTHxHEIGHT format (e.g., 128x128)";
          }
          return true;
        },
      });

      const iconTheme = await input({
        message: "Icon theme (light, dark, or custom - optional):",
        default: "",
      });

      icons.push({
        src: iconSrc,
        size: iconSize,
        ...(iconTheme.trim() ? { theme: iconTheme.trim() } : {}),
      });

      addMoreIcons = await confirm({
        message: "Add another icon entry?",
        default: false,
      });
    }
  }

  const addScreenshots = await confirm({
    message: "Add screenshots?",
    default: false,
  });

  const screenshots: string[] = [];
  if (addScreenshots) {
    let addMore = true;
    while (addMore) {
      const screenshot = await input({
        message: "Screenshot file path (relative to manifest):",
        validate: (value) => {
          if (!value.trim()) return "Screenshot path is required";
          if (value.includes("..")) return "Relative paths cannot include '..'";
          return true;
        },
      });
      screenshots.push(screenshot);

      addMore = await confirm({
        message: "Add another screenshot?",
        default: false,
      });
    }
  }

  return { icon, icons, screenshots };
}

export async function promptLocalization() {
  const configureLocalization = await confirm({
    message: "Configure localization resources?",
    default: false,
  });

  if (!configureLocalization) {
    return undefined;
  }

  const placeholderRegex = /\$\{locale\}/i;

  const resourcesPath = await input({
    message:
      "Localization resources path (must include ${locale} placeholder):",
    default: "resources/${locale}.json",
    validate: (value) => {
      if (!value.trim()) {
        return "Resources path is required";
      }
      if (value.includes("..")) {
        return "Relative paths cannot include '..'";
      }
      if (!placeholderRegex.test(value)) {
        return "Path must include a ${locale} placeholder";
      }
      return true;
    },
  });

  const defaultLocale = await input({
    message: "Default locale (BCP 47, e.g., en-US):",
    default: "en-US",
    validate: (value) => {
      if (!value.trim()) {
        return "Default locale is required";
      }
      if (!/^[A-Za-z0-9]{2,8}(?:-[A-Za-z0-9]{1,8})*$/.test(value)) {
        return "Default locale must follow BCP 47 (e.g., en-US or zh-Hans)";
      }
      return true;
    },
  });

  return {
    resources: resourcesPath,
    default_locale: defaultLocale,
  };
}

export async function promptCompatibility(
  serverType: "node" | "python" | "binary",
) {
  const addCompatibility = await confirm({
    message: "Add compatibility constraints?",
    default: false,
  });

  if (!addCompatibility) {
    return undefined;
  }

  const addPlatforms = await confirm({
    message: "Specify supported platforms?",
    default: false,
  });

  let platforms: ("darwin" | "win32" | "linux")[] | undefined;
  if (addPlatforms) {
    const selectedPlatforms: ("darwin" | "win32" | "linux")[] = [];

    const supportsDarwin = await confirm({
      message: "Support macOS (darwin)?",
      default: true,
    });
    if (supportsDarwin) selectedPlatforms.push("darwin");

    const supportsWin32 = await confirm({
      message: "Support Windows (win32)?",
      default: true,
    });
    if (supportsWin32) selectedPlatforms.push("win32");

    const supportsLinux = await confirm({
      message: "Support Linux?",
      default: true,
    });
    if (supportsLinux) selectedPlatforms.push("linux");

    platforms = selectedPlatforms.length > 0 ? selectedPlatforms : undefined;
  }

  let runtimes: { python?: string; node?: string } | undefined;
  if (serverType !== "binary") {
    const addRuntimes = await confirm({
      message: "Specify runtime version constraints?",
      default: false,
    });

    if (addRuntimes) {
      if (serverType === "python") {
        const pythonVersion = await input({
          message: "Python version constraint (e.g., >=3.8,<4.0):",
          validate: (value) =>
            value.trim().length > 0 || "Python version constraint is required",
        });
        runtimes = { python: pythonVersion };
      } else if (serverType === "node") {
        const nodeVersion = await input({
          message: "Node.js version constraint (e.g., >=16.0.0):",
          validate: (value) =>
            value.trim().length > 0 || "Node.js version constraint is required",
        });
        runtimes = { node: nodeVersion };
      }
    }
  }

  return {
    ...(platforms ? { platforms } : {}),
    ...(runtimes ? { runtimes } : {}),
  };
}

export async function promptUserConfig() {
  const addUserConfig = await confirm({
    message: "Add user-configurable options?",
    default: false,
  });

  if (!addUserConfig) {
    return {};
  }

  const userConfig: Record<
    string,
    {
      type: "string" | "number" | "boolean" | "directory" | "file";
      title: string;
      description: string;
      required?: boolean;
      default?: string | number | boolean | string[];
      multiple?: boolean;
      sensitive?: boolean;
      min?: number;
      max?: number;
    }
  > = {};

  let addMore = true;
  while (addMore) {
    const optionKey = await input({
      message: "Configuration option key (unique identifier):",
      validate: (value) => {
        if (!value.trim()) return "Key is required";
        if (userConfig[value]) return "Key must be unique";
        return true;
      },
    });

    const optionType = (await select({
      message: "Option type:",
      choices: [
        { name: "String", value: "string" },
        { name: "Number", value: "number" },
        { name: "Boolean", value: "boolean" },
        { name: "Directory", value: "directory" },
        { name: "File", value: "file" },
      ],
    })) as "string" | "number" | "boolean" | "directory" | "file";

    const optionTitle = await input({
      message: "Option title (human-readable name):",
      validate: (value) => value.trim().length > 0 || "Title is required",
    });

    const optionDescription = await input({
      message: "Option description:",
      validate: (value) => value.trim().length > 0 || "Description is required",
    });

    const optionRequired = await confirm({
      message: "Is this option required?",
      default: false,
    });

    const optionSensitive = await confirm({
      message: "Is this option sensitive (like a password)?",
      default: false,
    });

    // Build the option object
    const option: {
      type: "string" | "number" | "boolean" | "directory" | "file";
      title: string;
      description: string;
      required: boolean;
      sensitive: boolean;
      default?: string | number | boolean | string[];
      min?: number;
      max?: number;
    } = {
      type: optionType,
      title: optionTitle,
      description: optionDescription,
      required: optionRequired,
      sensitive: optionSensitive,
    };

    // Add default value if not required
    if (!optionRequired) {
      let defaultValue: string | number | boolean | string[] | undefined;
      if (optionType === "boolean") {
        defaultValue = await confirm({
          message: "Default value:",
          default: false,
        });
      } else if (optionType === "number") {
        const defaultStr = await input({
          message: "Default value (number):",
          validate: (value) => {
            if (!value.trim()) return true;
            return !isNaN(Number(value)) || "Must be a valid number";
          },
        });
        defaultValue = defaultStr ? Number(defaultStr) : undefined;
      } else {
        defaultValue = await input({
          message: "Default value (optional):",
        });
      }
      if (defaultValue !== undefined && defaultValue !== "") {
        option.default = defaultValue;
      }
    }

    // Add constraints for number types
    if (optionType === "number") {
      const addConstraints = await confirm({
        message: "Add min/max constraints?",
        default: false,
      });
      if (addConstraints) {
        const min = await input({
          message: "Minimum value (optional):",
          validate: (value) => {
            if (!value.trim()) return true;
            return !isNaN(Number(value)) || "Must be a valid number";
          },
        });
        const max = await input({
          message: "Maximum value (optional):",
          validate: (value) => {
            if (!value.trim()) return true;
            return !isNaN(Number(value)) || "Must be a valid number";
          },
        });
        if (min) option.min = Number(min);
        if (max) option.max = Number(max);
      }
    }

    userConfig[optionKey] = option;

    addMore = await confirm({
      message: "Add another configuration option?",
      default: false,
    });
  }

  return userConfig;
}

export function buildManifest(
  basicInfo: {
    name: string;
    displayName: string;
    version: string;
    description: string;
    authorName: string;
  },
  longDescription: string | undefined,
  authorInfo: {
    authorEmail: string;
    authorUrl: string;
  },
  urls: {
    homepage: string;
    documentation: string;
    support: string;
  },
  visualAssets: {
    icon: string;
    icons: Array<{
      src: string;
      size: string;
      theme?: string;
    }>;
    screenshots: string[];
  },
  serverConfig: {
    serverType: "node" | "python" | "binary";
    entryPoint: string;
    mcp_config: {
      command: string;
      args: string[];
      env?: Record<string, string>;
    };
  },
  tools: Array<{ name: string; description?: string }>,
  toolsGenerated: boolean,
  prompts: Array<{
    name: string;
    description?: string;
    arguments?: string[];
    text: string;
  }>,
  promptsGenerated: boolean,
  compatibility:
    | {
        claude_desktop?: string;
        platforms?: ("darwin" | "win32" | "linux")[];
        runtimes?: { python?: string; node?: string };
      }
    | undefined,
  userConfig: Record<
    string,
    {
      type: "string" | "number" | "boolean" | "directory" | "file";
      title: string;
      description: string;
      required?: boolean;
      default?: string | number | boolean | string[];
      multiple?: boolean;
      sensitive?: boolean;
      min?: number;
      max?: number;
    }
  >,
  optionalFields: {
    keywords: string;
    license: string;
    repository?: { type: string; url: string };
  },
  // localization?: {
  //   resources: string;
  //   default_locale: string;
  // },
): z.infer<typeof McpbManifestSchema> {
  const { name, displayName, version, description, authorName } = basicInfo;
  const { authorEmail, authorUrl } = authorInfo;
  const { serverType, entryPoint, mcp_config } = serverConfig;
  const { keywords, license, repository } = optionalFields;

  return {
    manifest_version: DEFAULT_MANIFEST_VERSION,
    name,
    ...(displayName && displayName !== name
      ? { display_name: displayName }
      : {}),
    version,
    description,
    ...(longDescription ? { long_description: longDescription } : {}),
    author: {
      name: authorName,
      ...(authorEmail ? { email: authorEmail } : {}),
      ...(authorUrl ? { url: authorUrl } : {}),
    },
    ...(urls.homepage ? { homepage: urls.homepage } : {}),
    ...(urls.documentation ? { documentation: urls.documentation } : {}),
    ...(urls.support ? { support: urls.support } : {}),
    ...(visualAssets.icon ? { icon: visualAssets.icon } : {}),
    ...(visualAssets.icons.length > 0 ? { icons: visualAssets.icons } : {}),
    ...(visualAssets.screenshots.length > 0
      ? { screenshots: visualAssets.screenshots }
      : {}),
    // ...(localization ? { localization } : {}),
    server: {
      type: serverType,
      entry_point: entryPoint,
      mcp_config,
    },
    ...(tools.length > 0 ? { tools } : {}),
    ...(toolsGenerated ? { tools_generated: true } : {}),
    ...(prompts.length > 0 ? { prompts } : {}),
    ...(promptsGenerated ? { prompts_generated: true } : {}),
    ...(compatibility ? { compatibility } : {}),
    ...(Object.keys(userConfig).length > 0 ? { user_config: userConfig } : {}),
    ...(keywords
      ? {
          keywords: keywords
            .split(",")
            .map((k) => k.trim())
            .filter((k) => k),
        }
      : {}),
    ...(license ? { license } : {}),
    ...(repository ? { repository } : {}),
  };
}

export function printNextSteps() {
  console.log("\nNext steps:");
  console.log(
    `1. Ensure all your production dependencies are in this directory`,
  );
  console.log(`2. Run 'mcpb pack' to create your .mcpb file`);
}

export async function initExtension(
  targetPath: string = process.cwd(),
  nonInteractive = false,
): Promise<boolean> {
  const resolvedPath = resolve(targetPath);
  const manifestPath = join(resolvedPath, "manifest.json");

  if (existsSync(manifestPath)) {
    if (nonInteractive) {
      console.log(
        "manifest.json already exists. Use --force to overwrite in non-interactive mode.",
      );
      return false;
    }
    const overwrite = await confirm({
      message: "manifest.json already exists. Overwrite?",
      default: false,
    });
    if (!overwrite) {
      console.log("Cancelled");
      return false;
    }
  }

  if (!nonInteractive) {
    console.log(
      "This utility will help you create a manifest.json file for your MCPB bundle.",
    );
    console.log("Press ^C at any time to quit.\n");
  } else {
    console.log("Creating manifest.json with default values...");
  }

  try {
    const packageData = readPackageJson(resolvedPath);

    // Prompt for all information or use defaults
    const basicInfo = nonInteractive
      ? getDefaultBasicInfo(packageData, resolvedPath)
      : await promptBasicInfo(packageData, resolvedPath);
    const longDescription = nonInteractive
      ? undefined
      : await promptLongDescription(basicInfo.description);
    const authorInfo = nonInteractive
      ? getDefaultAuthorInfo(packageData)
      : await promptAuthorInfo(packageData);
    const urls = nonInteractive
      ? { homepage: "", documentation: "", support: "" }
      : await promptUrls();
    const visualAssets = nonInteractive
      ? { icon: "", icons: [], screenshots: [] }
      : await promptVisualAssets();
    // const localization = nonInteractive
    //   ? undefined
    //   : await promptLocalization();
    const serverConfig = nonInteractive
      ? getDefaultServerConfig(packageData)
      : await promptServerConfig(packageData);
    const toolsData = nonInteractive
      ? { tools: [], toolsGenerated: false }
      : await promptTools();
    const promptsData = nonInteractive
      ? { prompts: [], promptsGenerated: false }
      : await promptPrompts();
    const compatibility = nonInteractive
      ? undefined
      : await promptCompatibility(serverConfig.serverType);
    const userConfig = nonInteractive ? {} : await promptUserConfig();
    const optionalFields = nonInteractive
      ? getDefaultOptionalFields(packageData)
      : await promptOptionalFields(packageData);

    // Build manifest
    const manifest = buildManifest(
      basicInfo,
      longDescription,
      authorInfo,
      urls,
      visualAssets,
      serverConfig,
      toolsData.tools,
      toolsData.toolsGenerated,
      promptsData.prompts,
      promptsData.promptsGenerated,
      compatibility,
      userConfig,
      optionalFields,
    );

    // Write manifest
    writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
    console.log(`\nCreated manifest.json at ${manifestPath}`);

    printNextSteps();

    return true;
  } catch (error) {
    if (error instanceof Error && error.message.includes("User force closed")) {
      console.log("\nCancelled");
      return false;
    }
    throw error;
  }
}
